﻿using System;
using System.Collections.Generic;
using System.Reflection;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Utilities.Tests
{
    [TestClass()]
    public class TypeExtensionsTest
    {
        private TestContext testContextInstance;

        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion

        public struct ComplexStruct
        {
        }

        public class TypeExtensionsTestObject
        {
            public int?[] NullableIntArray { get; set; }

            public ComplexStruct?[] ComplexTypeArray { get; set; }
            
            public List<Guid?> NullableGuidList { get; set; }

            public Dictionary<string, DateTime?> NullableDateTimeDictionary { get; set; }

            public enum SomeEnum
            {
                EnumItem1 = 1,
                EnumItem2 = 2,
                EnumItem3 = 3
            }

            public SomeEnum EnumField { get; set; }
        }

        [TestMethod()]
        public void TypeExtensionsUnwrapTypeTest()
        {
            Type type = typeof(TypeExtensionsTestObject);

            PropertyInfo propertyInfo = type.GetProperty("NullableIntArray");
            Type propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(typeof(int), propertyType.UnwrapType());

            propertyInfo = type.GetProperty("ComplexTypeArray");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(typeof(ComplexStruct), propertyType.UnwrapType());

            propertyInfo = type.GetProperty("NullableGuidList");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(typeof(Guid), propertyType.UnwrapType());

            propertyInfo = type.GetProperty("NullableDateTimeDictionary");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(typeof(DateTime), propertyType.UnwrapType());
        }

        [TestMethod()]
        public void TypeExtensionsGetDefaultTest()
        {
            Type type = typeof(TypeExtensionsTestObject);

            PropertyInfo propertyInfo = type.GetProperty("NullableIntArray");
            Type propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(null, propertyType.GetDefault());

            propertyInfo = type.GetProperty("ComplexTypeArray");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(null, propertyType.GetDefault());

            propertyInfo = type.GetProperty("NullableGuidList");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(null, propertyType.GetDefault());

            propertyInfo = type.GetProperty("NullableDateTimeDictionary");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(null, propertyType.GetDefault());

            // Enumeration
            propertyInfo = type.GetProperty("EnumField");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual((TypeExtensionsTestObject.SomeEnum)0, propertyType.GetDefault()); // If an enumeration element has the value of zero it will be set

            Assert.AreEqual(Guid.Empty, typeof(Guid).GetDefault());
            Assert.AreEqual(DateTime.MinValue, typeof(DateTime).GetDefault());
            Assert.AreEqual(0, typeof(int).GetDefault());
            Assert.AreEqual(null, typeof(string).GetDefault());
        }

        [TestMethod()]
        public void TypeExtensionsFromStringTest()
        {
            Type type = typeof(TypeExtensionsTestObject);

            PropertyInfo propertyInfo = type.GetProperty("NullableIntArray");
            Type propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(null, propertyType.FromString(null));

            propertyInfo = type.GetProperty("ComplexTypeArray");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(null, propertyType.FromString(null));

            propertyInfo = type.GetProperty("NullableGuidList");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(null, propertyType.FromString(null));

            propertyInfo = type.GetProperty("NullableDateTimeDictionary");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(null, propertyType.FromString(null));

            // Enumeration
            propertyInfo = type.GetProperty("EnumField");
            propertyType = propertyInfo.PropertyType;
            Assert.AreEqual(TypeExtensionsTestObject.SomeEnum.EnumItem2, propertyType.FromString("2"));
            Assert.AreEqual(TypeExtensionsTestObject.SomeEnum.EnumItem2, propertyType.FromString("EnumItem2"));

            // Guid
            Assert.AreEqual(Guid.Empty, typeof(Guid).FromString(null));

            Guid guid = Guid.NewGuid();
            Assert.AreEqual(guid, typeof(Guid).FromString(guid.ToString()));

            // Date time
            Assert.AreEqual(DateTime.MinValue, typeof(DateTime).FromString(null));

            DateTime dateTime = new DateTime(2002, 5, 25);
            Assert.AreEqual(dateTime, typeof(DateTime).FromString(dateTime.ToString()));

            // int
            Assert.AreEqual(0, typeof(int).FromString(null));

            int i = 123;
            Assert.AreEqual(i, typeof(int).FromString(i.ToString()));

            // decimal
            Assert.AreEqual(0m, typeof(decimal).FromString(null));

            decimal d = 123m;
            Assert.AreEqual(d, typeof(decimal).FromString(d.ToString()));

            // bool
            Assert.AreEqual(false, typeof(bool).FromString(null));
            Assert.AreEqual(true, typeof(bool).FromString("true"));

            // string
            Assert.AreEqual(null, typeof(string).FromString(null));
            Assert.AreEqual("test", typeof(string).FromString("test"));

            // Nullable int
            Assert.AreEqual(null, typeof(int?).FromString(null));
            Assert.AreEqual(123, typeof(int?).FromString("123"));

            // Nullable decimal
            Assert.AreEqual(null, typeof(decimal?).FromString(null));
            Assert.AreEqual(d, typeof(decimal?).FromString(d.ToString()));

            // Nullable bool
            Assert.AreEqual(null, typeof(bool?).FromString(null));
            Assert.AreEqual(false, typeof(bool?).FromString("False"));

            // Nullable datetime
            Assert.AreEqual(null, typeof(DateTime?).FromString(null));
            Assert.AreEqual(dateTime, typeof(DateTime?).FromString(dateTime.ToString()));
        }
    }
}
